﻿using System;
using System.Net;
using System.Text;
using System.Collections.Generic;
using DXTools.UI.Modules.ODataQueryDesigner.Model;
using DXTools.Core.CrmSdk;


namespace DXTools.UI.Modules.ODataQueryDesigner
{
    public class ODataBuilder
    {

        public List<string> SelectAttributes { get; set; }
        public List<string> ExpandRelationships { get; set; }
        public List<string> OrderByAttributes { get; set; }
        public List<string> FilterAttributes { get; set; }

        public ODataBuilder()
        {
           
        }

        /// <summary>
        /// Buils the OData query string
        /// </summary>
        /// <param name="Select"></param>
        /// <param name="OrderBy"></param>
        /// <param name="Filter"></param>
        /// <param name="Expand"></param>
        /// <returns></returns>
        public string BuildQuery()
        {
            StringBuilder query = new StringBuilder();

            if (SelectAttributes != null && SelectAttributes.Count > 0)
            {
                query.Append("$select=");

                for (int counter = 0; counter < SelectAttributes.Count; counter++)
                {
                    if (counter == SelectAttributes.Count - 1)
                        query.Append(SelectAttributes[counter]);
                    else
                    {
                        query.Append(SelectAttributes[counter]);
                        query.Append(",");
                    }
                }
            }

            if (OrderByAttributes != null && OrderByAttributes.Count > 0)
            {
                if(query.Length>0)
                    query.Append("&");

                query.Append("$orderby=");

                for (int counter = 0; counter < OrderByAttributes.Count; counter++)
                {
                    if (counter == OrderByAttributes.Count - 1)
                        query.Append(OrderByAttributes[counter]);
                    else
                    {
                        query.Append(OrderByAttributes[counter]);
                        query.Append(",");
                    }
                }
            }

            if (ExpandRelationships != null &&ExpandRelationships.Count > 0)
            {
                if (query.Length > 0)
                    query.Append("&");

                query.Append("$expand=");

                for (int counter = 0; counter < ExpandRelationships.Count; counter++)
                {
                    if (counter == ExpandRelationships.Count - 1)
                        query.Append(ExpandRelationships[counter]);
                    else
                    {
                        query.Append(ExpandRelationships[counter]);
                        query.Append(",");
                    }
                }
            }

            if (FilterAttributes != null && FilterAttributes.Count > 0)
            {
                if (query.Length > 0)
                    query.Append("&");

                query.Append("$filter=");

                for (int counter = 0; counter < FilterAttributes.Count; counter++)
                {
                    query.Append(FilterAttributes[counter]);
                }
            }   
            
            return query.ToString();
        }


        public void ProcessSelectAttributesAndExpands(EntityClass Entity)
        {
            SelectAttributes = new List<string>();
            ExpandRelationships = new List<string>();
           

            if (Entity.Attributes.Count > 0)
            {
               
                if (Entity.IsSelectAllAttributes == true)
                {
                    SelectAttributes.Add("*");
                }
                else
                {
                    foreach (AttributeClass item in Entity.Attributes)
                    {
                        if (item.IsSelected == true)
                        {
                            if (item.SchemaName != "{Select All)")
                            {
                                SelectAttributes.Add(item.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process select attributes
            if (Entity.Relationships_ManyToOne.Count > 0)
            {
                foreach (RelationshipClass rel in Entity.Relationships_ManyToOne)
                {
                    if (rel.Entity != null && rel.Entity.Attributes != null && rel.Entity.Attributes != null && rel.Entity.Attributes.Count > 0)
                    {
                        if (rel.Entity.IsSelectAllAttributes == true)
                        {
                            SelectAttributes.Add(rel.SchemaName);
                            // add related to expand
                            if (!ExpandRelationships.Contains(rel.SchemaName))
                                ExpandRelationships.Add(rel.SchemaName);
                        }
                        else
                        {
                            bool addRelToExpand = false;
                            foreach (AttributeClass item in rel.Entity.Attributes)
                            {
                                if (item.IsSelected == true)
                                {
                                    if (item.SchemaName != "{Select All)")
                                    {
                                        SelectAttributes.Add(rel.SchemaName + "/" + item.SchemaName);
                                        addRelToExpand = true;
                                    }
                                }
                            }
                            if (addRelToExpand && !ExpandRelationships.Contains(rel.SchemaName))
                            {
                                ExpandRelationships.Add(rel.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process select attributes
            if (Entity.Relationships_OneToMany.Count > 0)
            {
                foreach (RelationshipClass rel in Entity.Relationships_OneToMany)
                {
                    if (rel.Entity != null && rel.Entity.Attributes != null && rel.Entity.Attributes != null && rel.Entity.Attributes.Count > 0)
                    {
                        if (rel.Entity.IsSelectAllAttributes == true)
                        {
                            SelectAttributes.Add(rel.SchemaName);
                            // add related to expand
                            if (!ExpandRelationships.Contains(rel.SchemaName))
                                ExpandRelationships.Add(rel.SchemaName);
                        }
                        else
                        {
                            bool addRelToExpand = false;
                            foreach (AttributeClass item in rel.Entity.Attributes)
                            {
                                if (item.IsSelected == true)
                                {
                                    if (item.SchemaName != "{Select All)")
                                    {
                                        SelectAttributes.Add(rel.SchemaName + "/" + item.SchemaName);
                                        addRelToExpand = true;
                                    }
                                }
                            }
                            if (addRelToExpand && !ExpandRelationships.Contains(rel.SchemaName))
                            {
                                ExpandRelationships.Add(rel.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process select attributes
            if (Entity.Relationships_ManyToMany.Count > 0)
            {
                foreach (RelationshipClass rel in Entity.Relationships_ManyToMany)
                {
                    if (rel.Entity != null && rel.Entity.Attributes != null && rel.Entity.Attributes != null && rel.Entity.Attributes.Count > 0)
                    {
                        if (rel.Entity.IsSelectAllAttributes == true)
                        {
                            SelectAttributes.Add(rel.SchemaName);
                            // add related to expand
                            if (!ExpandRelationships.Contains(rel.SchemaName))
                                ExpandRelationships.Add(rel.SchemaName);
                        }
                        else
                        {
                            bool addRelToExpand = false;
                            foreach (AttributeClass item in rel.Entity.Attributes)
                            {
                                if (item.IsSelected == true)
                                {
                                    if (item.SchemaName != "{Select All)")
                                    {
                                        SelectAttributes.Add(rel.SchemaName + "/" + item.SchemaName);
                                        addRelToExpand = true;
                                    }
                                }
                            }
                            if (addRelToExpand && !ExpandRelationships.Contains(rel.SchemaName))
                            {
                                ExpandRelationships.Add(rel.SchemaName);
                            }
                        }
                    }
                }
            }
        }


        public void ProcessOrderByAttributes(AttributeClass SelectedSortBy1Attribute, SortDirection SortBy1Direction, AttributeClass SelectedSortBy2Attribute, SortDirection SortBy2Direction)
        {
            OrderByAttributes = new List<string>();
           
            if (SelectedSortBy1Attribute != null && !String.IsNullOrEmpty(SelectedSortBy1Attribute.SchemaName))
            {
                OrderByAttributes.Add(SelectedSortBy1Attribute.SchemaName + " " + SortBy1Direction.ToString());
            }

            if (SelectedSortBy2Attribute != null && !String.IsNullOrEmpty(SelectedSortBy2Attribute.SchemaName))
            {
                OrderByAttributes.Add(SelectedSortBy2Attribute.SchemaName + " " + SortBy2Direction.ToString());
            }

        }


        public void ProcessFilters(EntityClass Entity)
        {
            FilterAttributes = new List<string>();

            if (Entity.Filters.Count > 1)
            {
                string filterString = "";
                string attribute = "";

                foreach (FilterClass item in Entity.Filters)
                {
                    filterString = "";

                    if (item.SelectedAttribute != null)
                    {
                        if (item.IsPrimaryGroupAttribute)
                            filterString += "(";

                        switch (item.SelectedAttribute.AttributeType.Value)
                        {
                            case AttributeTypeCode.Picklist:
                            //case AttributeTypeCode.Double:
                            //case AttributeTypeCode.Decimal:
                            //case AttributeTypeCode.Integer:
                            case AttributeTypeCode.Money:
                            case AttributeTypeCode.State:
                            case AttributeTypeCode.Status:
                                //case AttributeTypeCode.BigInt:
                                attribute = String.Format("{0}/{1}", item.SelectedAttribute.SchemaName, "Value");
                                break;
                            case AttributeTypeCode.Lookup:
                            case AttributeTypeCode.Customer:
                            case AttributeTypeCode.Owner:
                                attribute = String.Format("{0}/{1}", item.SelectedAttribute.SchemaName, "Id");
                                break;
                            default:
                                attribute = item.SelectedAttribute.SchemaName;
                                break;
                        }

                        switch (item.FilterOperator)
                        {
                            case "Equals":
                                filterString += String.Format("{0} eq {1}", attribute, FormatValue(item));
                                break;
                            case "Does Not Equal":
                                filterString += String.Format("{0} ne {1}", attribute, FormatValue(item));
                                break;
                            case "Contains":
                                filterString += String.Format("substringof({0},{1})", FormatValue(item), attribute);
                                break;
                            case "Does Not Contain":
                                filterString += String.Format("not substringof({0},{1})", FormatValue(item), attribute);
                                break;
                            case "Begins With":
                                filterString += String.Format("startswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Does Not Begin With":
                                filterString += String.Format("not startswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Ends With":
                                filterString += String.Format("endswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Does Not End With":
                                filterString += String.Format("not endswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Is Greater Than":
                                filterString += String.Format("{0} gt {1}", attribute, FormatValue(item));
                                break;
                            case "Is Greater That or Equal To":
                            case "On Or After":
                                filterString += String.Format("{0} ge {1}", attribute, FormatValue(item));
                                break;
                            case "Is Less Than":
                                filterString += String.Format("{0} lt {1}", attribute, FormatValue(item));
                                break;
                            case "Is Less Than or Equal To":
                            case "On Or Before":
                                filterString += String.Format("{0} le {1}", attribute, FormatValue(item));
                                break;
                            case "Contains Data":
                                filterString += String.Format("{0} ne null", attribute);
                                break;
                            case "Does Not Contain Data":
                                filterString += String.Format("{0} eq null", attribute);
                                break;
                        }


                        if (item.IsFinalGroupAttribute)
                        {
                            filterString += ")";

                            if (Entity.Filters.Count > (Entity.Filters.IndexOf(item) + 1) && Entity.Filters[Entity.Filters.IndexOf(item) + 1].SelectedAttribute != null)
                                filterString += (item.GroupLogicalOperator == "AND") ? " and " : " or ";

                        }
                        else
                        {
                            if (Entity.Filters.Count > (Entity.Filters.IndexOf(item) + 1) && Entity.Filters[Entity.Filters.IndexOf(item) + 1].SelectedAttribute != null)
                                filterString += (item.LogicalOperator == "AND") ? " and " : " or ";
                        }

                        FilterAttributes.Add(filterString);
                    }
                }
            }
        }


        public void ProcessFiltersWithVariables(EntityClass Entity)
        {
            FilterAttributes = new List<string>();

            if (Entity.Filters.Count > 1)
            {
                string filterString = "";
                string attribute = "";

                foreach (FilterClass item in Entity.Filters)
                {
                    filterString = "";

                    if (item.SelectedAttribute != null)
                    {
                        if (item.IsPrimaryGroupAttribute)
                            filterString += "(";

                        switch (item.SelectedAttribute.AttributeType.Value)
                        {
                            case AttributeTypeCode.Picklist:
                            //case AttributeTypeCode.Double:
                            //case AttributeTypeCode.Decimal:
                            //case AttributeTypeCode.Integer:
                            case AttributeTypeCode.Money:
                            case AttributeTypeCode.State:
                            case AttributeTypeCode.Status:
                                //case AttributeTypeCode.BigInt:
                                attribute = String.Format("{0}/{1}", item.SelectedAttribute.SchemaName, "Value");
                                break;
                            case AttributeTypeCode.Lookup:
                            case AttributeTypeCode.Customer:
                            case AttributeTypeCode.Owner:
                                attribute = String.Format("{0}/{1}", item.SelectedAttribute.SchemaName, "Id");
                                break;
                            default:
                                attribute = item.SelectedAttribute.SchemaName;
                                break;
                        }

                        switch (item.FilterOperator)
                        {
                            case "Equals":
                                filterString += String.Format("{0} eq {1}", attribute, FormatValueAsVariable(item));
                                break;
                            case "Does Not Equal":
                                filterString += String.Format("{0} ne {1}", attribute, FormatValueAsVariable(item));
                                break;
                            case "Contains":
                                filterString += String.Format("substringof({0},{1})", FormatValueAsVariable(item), attribute);
                                break;
                            case "Does Not Contain":
                                filterString += String.Format("not substringof({0},{1})", FormatValueAsVariable(item), attribute);
                                break;
                            case "Begins With":
                                filterString += String.Format("startswith({0},{1})", attribute, FormatValueAsVariable(item));
                                break;
                            case "Does Not Begin With":
                                filterString += String.Format("not startswith({0},{1})", attribute, FormatValueAsVariable(item));
                                break;
                            case "Ends With":
                                filterString += String.Format("endswith({0},{1})", attribute, FormatValueAsVariable(item));
                                break;
                            case "Does Not End With":
                                filterString += String.Format("not endswith({0},{1})", attribute, FormatValueAsVariable(item));
                                break;
                            case "Is Greater Than":
                                filterString += String.Format("{0} gt {1}", attribute, FormatValueAsVariable(item));
                                break;
                            case "Is Greater That or Equal To":
                            case "On Or After":
                                filterString += String.Format("{0} ge {1}", attribute, FormatValueAsVariable(item));
                                break;
                            case "Is Less Than":
                                filterString += String.Format("{0} lt {1}", attribute, FormatValueAsVariable(item));
                                break;
                            case "Is Less Than or Equal To":
                            case "On Or Before":
                                filterString += String.Format("{0} le {1}", attribute, FormatValueAsVariable(item));
                                break;
                            case "Contains Data":
                                filterString += String.Format("{0} ne null", attribute);
                                break;
                            case "Does Not Contain Data":
                                filterString += String.Format("{0} eq null", attribute);
                                break;
                        }


                        if (item.IsFinalGroupAttribute)
                        {
                            filterString += ")";

                            if (Entity.Filters.Count > (Entity.Filters.IndexOf(item) + 1) && Entity.Filters[Entity.Filters.IndexOf(item) + 1].SelectedAttribute != null)
                                filterString += (item.GroupLogicalOperator == "AND") ? " and " : " or ";

                        }
                        else
                        {
                            if (Entity.Filters.Count > (Entity.Filters.IndexOf(item) + 1) && Entity.Filters[Entity.Filters.IndexOf(item) + 1].SelectedAttribute != null)
                                filterString += (item.LogicalOperator == "AND") ? " and " : " or ";
                        }

                        FilterAttributes.Add(filterString);
                    }
                }
            }
        }


        /// <summary>
        /// Formats the value of required such as guid'' and datetime''
        /// </summary>
        /// <param name="Filter"></param>
        /// <returns></returns>
        private String FormatValue(FilterClass Filter)
        {
            String tmp = Filter.Value;

            switch (Filter.SelectedAttribute.AttributeType)
            {
                case AttributeTypeCode.DateTime:
                    tmp = String.Format("datetime'{0}'", DateTime.Parse(Filter.Value).ToString("yyyy-MM-ddThh:mm:ss"));
                    break;
                case AttributeTypeCode.Lookup:
                case AttributeTypeCode.Customer:
                case AttributeTypeCode.Uniqueidentifier:
                case AttributeTypeCode.Owner:
                    tmp = String.Format("guid'{0}'", Filter.Value);
                    break;
                case AttributeTypeCode.String:
                case AttributeTypeCode.Memo:
                    tmp = String.Format("'{0}'", Filter.Value);
                    break;
            }

            return tmp;


        }

        /// <summary>
        /// Formats the value of required such as guid'' and datetime''
        /// </summary>
        /// <param name="Filter"></param>
        /// <returns></returns>
        private String FormatValueAsVariable(FilterClass Filter)
        {
            String tmp = Filter.Value;

            switch (Filter.SelectedAttribute.AttributeType)
            {
                case AttributeTypeCode.DateTime:
                    tmp = String.Format("datetime'\"+{0}+\"'", Filter.SelectedAttribute.SchemaName);
                    break;
                case AttributeTypeCode.Lookup:
                case AttributeTypeCode.Customer:
                case AttributeTypeCode.Uniqueidentifier:
                case AttributeTypeCode.Owner:
                    tmp = String.Format("guid'\"+{0}+\"'", Filter.SelectedAttribute.SchemaName);
                    break;
                case AttributeTypeCode.String:
                case AttributeTypeCode.Memo:
                    tmp = String.Format("'\"+{0}+\"'", Filter.SelectedAttribute.SchemaName);
                    break;
                default:
                     tmp = String.Format("\"+{0}+\"", Filter.SelectedAttribute.SchemaName);
                    break;
            }

            return tmp;


        }
    }



   

}
